//
//  VideoPlayer.swift
//  UILibrarysKit
//
//  Created by TSYH on 2024/5/31.
//

import AVFoundation
import UIKit


public class VideoPlayer: UIView , VideoGestureViewDelegate{
    public func swipeDownGesture() {
        self.delegate?.playerSwipeDown()
    }
    
    public weak var delegate: VideoPlayerDelegate?

    /// 播放属性
    private var playerLayer: AVPlayerLayer = .init()
    public weak var controlView: VideoPlayerControlView? {
        didSet {
            guard let view = controlView else { return }
            controlView?.ownerPlayer = self
            if let gestureView = gestureView {
                gestureView.addSubview(view)
            } else {
                addSubview(view)
            }
        }
    }

    // 手势控制view
    var gestureView: VideoGestureView?

    public lazy var playMode: PlayMode = .video {
        didSet {
            switch playMode {
            case .video:
                connectPlayerLayer()
            case .audio:
                audioPlayerLayer?.isHidden = false
                disconnectPlayerLayer()
            }
        }
    }

    public var audioPlayerLayer: UIView? {
        didSet {
            if let v = audioPlayerLayer {
                insertSubview(v, at: 0)
                v.isUserInteractionEnabled = false
                v.isHidden = playMode != .audio
            }
        }
    }

    /// 视频跳转秒数置0
    lazy var seekTime: TimeInterval = 0
    lazy var isSliderSliding: Bool = false

    public internal(set) var player: MinePlayer
    public required init(player: MinePlayer) {
        self.player = player
        super.init(frame: .zero)
        player.add(self)

        gestureView = VideoGestureView(ownerPlayer: self)
        gestureView?.delegate = self
        addSubview(gestureView!)
        configPlayer()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public var videoGravity = AVLayerVideoGravity.resizeAspect {
        didSet {
            playerLayer.videoGravity = videoGravity
        }
    }

    public var aspectRatio: BMPlayerAspectRatio = .default {
        didSet {
            setNeedsLayout()
        }
    }

    public func play() {
        player.play()
    }

    public func pause() {
        player.pause()
    }

    public func togglePlay() {
        player.togglePlay()
    }

    public var rate: Float {
        return player.rate
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
        playerLayer.player = nil
    }

    // MARK: - layoutSubviews

    override public func layoutSubviews() {
        super.layoutSubviews()
        gestureView?.frame = bounds
        controlView?.frame = bounds
        audioPlayerLayer?.frame = bounds

        switch aspectRatio {
        case .default:
            playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
            playerLayer.frame = bounds
            break
        case .sixteen2NINE:
            playerLayer.videoGravity = AVLayerVideoGravity.resize
            playerLayer.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.width / (16 / 9))
            break
        case .four2THREE:
            playerLayer.videoGravity = AVLayerVideoGravity.resize
            let _w = bounds.height * 4 / 3
            playerLayer.frame = CGRect(x: (bounds.width - _w) / 2, y: 0, width: _w, height: bounds.height)
            break
        }
    }

    public func reloadPlayerLayer() {
        connectPlayerLayer()
    }

    public func distoryPlayerLayer() {
        disconnectPlayerLayer()
    }

    public func prepareToDeinit() {
        seekTime = 0
        pause()
        // 移除原来的layer
        playerLayer.player = nil
    }

    public func seek(to secounds: TimeInterval, completion: (() -> Void)?) {
        player.seek(secounds, completion: completion)
    }

    fileprivate func configPlayer() {
        connectPlayerLayer()
        setNeedsLayout()
        layoutIfNeeded()

        NotificationCenter.default.addObserver(self, selector: #selector(connectPlayerLayer), name: UIApplication.willEnterForegroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(disconnectPlayerLayer), name: UIApplication.didEnterBackgroundNotification, object: nil)
    }

    @objc fileprivate func connectPlayerLayer() {
        switch playMode {
        case .video:
            audioPlayerLayer?.isHidden = true
            playerLayer.player = nil
            playerLayer.player = player.player
            playerLayer.videoGravity = videoGravity
            layer.insertSublayer(playerLayer, at: 0)

        case .audio:
            audioPlayerLayer?.isHidden = false
        }
    }

    @objc fileprivate func disconnectPlayerLayer() {
        playerLayer.player = nil
    }
}

extension VideoPlayer: MinePlayerDelegate {
    public func player(_ player: MinePlayer, resourceChanged resource: BMPlayerResource) {
        configPlayer()
        controlView?.player(self, resourceChanged: resource)
    }

    public func player(_ player: MinePlayer, stateChanged state: MinePlayerState) {
        delegate?.player(self, playerStateDidChange: state)
        controlView?.player(self, stateChanged: state)
    }

    public func player(_ player: MinePlayer, progressChanged current: Double, total: Double) {
        guard !isSliderSliding else { return }
        delegate?.player(self, playTimeDidChange: current, totalTime: total)
        controlView?.player(self, timeDidChange: current, total: total)
    }

    public func player(_ player: MinePlayer, loadedTimeDidChange duration: TimeInterval, total: TimeInterval) {
        delegate?.player(self, loadedTimeDidChange: duration, totalDuration: total)
        controlView?.player(self, didLoad: duration, total: total)
    }
}

// MARK: VideoGestureView

extension VideoPlayer {
    func gestureView(_ view: VideoGestureView, onTap gesture: UITapGestureRecognizer) {
        controlView?.player(self, toggleShow: true)
    }

    func gestureView(_ view: VideoGestureView, horizontalMoved value: CGFloat, state: UIGestureRecognizer.State) {
        switch state {
        case .began:
            // 记录滑动开始初值
            if let currentTime = player.player?.currentTime(), currentTime.timescale > 0 {
                seekTime = TimeInterval(currentTime.value) / TimeInterval(currentTime.timescale)
            }
        case .changed:
            isSliderSliding = true
            if let playerItem = player.player?.currentItem, CMTimeGetSeconds(playerItem.duration) > 0 {
                let totalDuration = CMTimeGetSeconds(playerItem.duration)
                // 每次滑动需要叠加时间，通过一定的比例，使滑动一直处于统一水平
                seekTime = seekTime + TimeInterval(value) / 100.0 * (TimeInterval(totalDuration) / 400)

                if seekTime >= totalDuration { seekTime = totalDuration }
                if seekTime <= 0 { seekTime = 0 }

                controlView?.player(self, seekTo: seekTime, total: totalDuration, isAdd: value > 0)
            }
        case .ended:
            seek(to: seekTime) { [weak self] in
                self?.seekTime = 0
                self?.isSliderSliding = false
            }
        default:
            break
        }
    }
}

// MARK:

public extension VideoPlayer {
    func controlView(controlView: UIView, didChooseDefinition index: Int) {
//        shouldSeekTo = currentPosition
//        playerLayer?.resetPlayer()
//        currentDefinition = index
//        playerLayer?.playAsset(asset: resource.definitions[index].avURLAsset)
    }

    func controlView(controlView: UIView, action: PlayerControlAction) {
        switch action {
        case .togglePlay:
            togglePlay()
        case .toggleMode:
            fullScreenButtonPressed()
        case .replay:
            player.seek(0, completion: nil)
            player.play()
        case .lock:
            gestureView?.isLock = true
        case .unlock:
            gestureView?.isLock = false
        }
    }

    func controlView(controlView: UIView, slider: UISlider, onSliderEvent event: UIControl.Event) {
        switch event {
        case .touchDown:
            isSliderSliding = true
        case .touchUpInside:
            guard let playItem = player.player?.currentItem else {
                return
            }
            let target = CMTimeGetSeconds(playItem.duration) * Double(slider.value)
            seek(to: target) { [weak self] in
                self?.isSliderSliding = false
                if self?.player.state != .playing {
                    self?.player.play()
                }
            }
        default:
            break
        }
    }

    func controlView(controlView: UIView, didChangeVideoAspectRatio value: BMPlayerAspectRatio) {
        aspectRatio = value
    }

    func controlView(controlView: UIView, didChangeVideoPlaybackRate rate: Float) {
        player.rate = rate
    }

    @objc fileprivate func fullScreenButtonPressed() {
        controlView?.player(self, orientationChangeTo: !isLandscape)
        if isLandscape {
            UIViewController.topViewController?.setDeviceTo(orientation: .portrait)
        } else {
            UIViewController.topViewController?.setDeviceTo(orientation: .landscapeRight)
        }
    }

    private var isLandscape: Bool {
        return UIApplication.shared.statusBarOrientation.isLandscape
    }
}
